`timescale 1ns/1ps
`default_nettype none

/* NOTE:
* - 扫描译码
*/

module scan_decoder (
    // system signal
    input  wire         I_sclk,  // 125M
    input  wire         I_rst_n,
    // config
    input  wire [4:0]   I_cfg_scan_max,      // 最大扫描id
    input  wire [2:0]   I_cfg_decode_type,   // 译码方式
    input  wire [15:0]  I_cfg_decode_param0, // 译码参数0
    input  wire [15:0]  I_cfg_decode_param1, // 译码参数1
    // control
    input  wire         I_scan_prep,   // 准备换行
    input  wire [4:0]   I_scan_num,    // 行id
    input  wire         I_scan_commit, // 执行换行
    // scan out
    output wire [4:0]   O_scan_out
);
//------------------------Parameter----------------------
localparam [2:0]
    MODE_138  = 0, // 138译码
    MODE_LOW  = 1, // 直接输出（低）
    MODE_HIGH = 2, // 直接输出（高）
    MODE_595  = 3, // 595译码
    MODE_5958 = 4, // 5958译码
    MODE_5266 = 5; // SM5266P

//------------------------Local signal-------------------
wire [9:0]  cfg_decode_param0;
wire [9:0]  cfg_decode_param1;

reg  [4:0]  scan_result;
reg  [4:0]  scan_out;

wire        flag_595;
wire        flag_5958;
wire        flag_5266;
reg  [9:0]  commit_cnt;

reg         clk_595;
reg         dat_595;

reg         clk_5958;
reg         dat_5958;
reg         oe_5958;

reg         bk_5266;
reg         din_5266;
reg         dck_5266;
reg         e_5266;
reg         f_5266;
reg         pad_flag_5266;
reg  [5:0]  pad_cnt_5266;
wire [9:0]  pad_param0;
wire [9:0]  pad_param1;

//------------------------Instantiation------------------

//------------------------Body---------------------------
assign cfg_decode_param0 = I_cfg_decode_param0[9:0];
assign cfg_decode_param1 = I_cfg_decode_param1[9:0];

assign O_scan_out = scan_out;

// scan_result
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        scan_result <= 1'b0;
    else if (I_scan_prep) begin
        case (I_cfg_decode_type)
            MODE_LOW: begin
                scan_result <= ~(5'b1 << I_scan_num[2:0]);
            end

            MODE_HIGH: begin
                scan_result <= 5'b1 << I_scan_num[2:0];
            end

            default: begin
                scan_result <= I_scan_num;
            end
        endcase
    end
end

// scan_out
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        scan_out <= 1'b0;
    else if (flag_595)
        scan_out <= {dat_595, clk_595};
    else if (flag_5958)
        scan_out <= {dat_5958, oe_5958, clk_5958};
    else if (flag_5266)
        scan_out <= {f_5266, e_5266, bk_5266, din_5266, dck_5266};
    else if (I_scan_commit)
        scan_out <= scan_result;
end

assign flag_595  = (I_cfg_decode_type == MODE_595);
assign flag_5958 = (I_cfg_decode_type == MODE_5958);
assign flag_5266 = (I_cfg_decode_type == MODE_5266);

// commit_cnt
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        commit_cnt <= 10'd1023;
    else if (I_scan_commit)
        commit_cnt <= 10'd1;
    else if (commit_cnt < 10'd1023)
        commit_cnt <= commit_cnt + 1'b1;
end

// clk_595
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        clk_595 <= 1'b0;
    else if (commit_cnt == cfg_decode_param0)
        clk_595 <= 1'b1;
    else if (commit_cnt == cfg_decode_param1)
        clk_595 <= 1'b0;
end

// dat_595
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        dat_595 <= 1'b1;
    else if (I_scan_commit) begin
        if (scan_result == I_cfg_scan_max)
            dat_595 <= 1'b0;
        else
            dat_595 <= 1'b1;
    end
end

// clk_5958
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        clk_5958 <= 1'b0;
    else if (commit_cnt == cfg_decode_param0)
        clk_5958 <= 1'b1;
    else if (commit_cnt == cfg_decode_param1)
        clk_5958 <= 1'b0;
end

// dat_5958
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        dat_5958 <= 1'b0;
    else if (I_scan_commit) begin
        if (scan_result == 1'b0)
            dat_5958 <= 1'b1;
        else
            dat_5958 <= 1'b0;
    end
end

// oe_5958
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        oe_5958 <= 1'b1;
    else if (I_scan_commit)
        oe_5958 <= 1'b1;
    else if (commit_cnt == cfg_decode_param1)
        oe_5958 <= 1'b0;
end

assign pad_param0 = cfg_decode_param0 + {~I_cfg_scan_max[2:0], 3'd7};
assign pad_param1 = cfg_decode_param1 + {~I_cfg_scan_max[2:0], 3'd7};

// bk_5266
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        bk_5266 <= 1'b0;
    else if (I_scan_commit)
        bk_5266 <= 1'b1;
    else if (pad_flag_5266 && commit_cnt == pad_param1)
        bk_5266 <= 1'b0;
    else if (!pad_flag_5266 && commit_cnt == cfg_decode_param1)
        bk_5266 <= 1'b0;
end

// din_5266
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        din_5266 <= 1'b0;
    else if (pad_flag_5266) begin
        if (pad_cnt_5266 == 1'b1)
            din_5266 <= 1'b1;
        else if (commit_cnt == pad_param1)
            din_5266 <= 1'b0;
    end
    else begin
        if (commit_cnt == 1'b1 && scan_result[2:0] == 1'b0)
            din_5266 <= 1'b1;
        else if (commit_cnt == cfg_decode_param1)
            din_5266 <= 1'b0;
    end
end

// dck_5266
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        dck_5266 <= 1'b0;
    else if (pad_cnt_5266[2:0] == 3'd7)
        dck_5266 <= 1'b1;
    else if (pad_cnt_5266[2:0] == 3'd3)
        dck_5266 <= 1'b0;
    else if (pad_flag_5266) begin
        if (commit_cnt == pad_param0)
            dck_5266 <= 1'b1;
        else if (commit_cnt == pad_param1)
            dck_5266 <= 1'b0;
    end
    else begin
        if (commit_cnt == cfg_decode_param0)
            dck_5266 <= 1'b1;
        else if (commit_cnt == cfg_decode_param1)
            dck_5266 <= 1'b0;
    end
end

// e_5266
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        e_5266 <= 1'b0;
    else if (pad_flag_5266 && commit_cnt == pad_param0)
        e_5266 <= scan_result[3];
    else if (!pad_flag_5266 && commit_cnt == cfg_decode_param0)
        e_5266 <= scan_result[3];
end

// f_5266
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        f_5266 <= 1'b0;
    else if (pad_flag_5266 && commit_cnt == pad_param0)
        f_5266 <= scan_result[4];
    else if (!pad_flag_5266 && commit_cnt == cfg_decode_param0)
        f_5266 <= scan_result[4];
end

// pad_flag_5266
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        pad_flag_5266 <= 1'b0;
    else if (I_cfg_scan_max <= 3'd7 || I_cfg_scan_max[2:0] == 3'd7)
        pad_flag_5266 <= 1'b0;
    else if (I_scan_commit) begin
        if (scan_result == 1'b0)
            pad_flag_5266 <= 1'b1;
        else
            pad_flag_5266 <= 1'b0;
    end
end

// pad_cnt_5266
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        pad_cnt_5266 <= 1'b0;
    else if (pad_flag_5266 && commit_cnt == cfg_decode_param0 - 2'd2)
        pad_cnt_5266 <= {~I_cfg_scan_max[2:0], 3'd0};
    else if (pad_cnt_5266 != 1'b0)
        pad_cnt_5266 <= pad_cnt_5266 - 1'b1;
end

endmodule

`default_nettype wire

// vim:set ts=4 sw=4 et fenc=utf-8 fdm=marker:
